#include "expparser.h"

#include "inc/sys_inc.h"

using namespace std;

#define DEBUG_MATCHx
#define CALC_INF_OUTPUTx

typedef vector<string> strings;

template<class T>
T calcBaseOperator(T l, T r, const std::string &oper)
{
    if (oper == "+")
        return l+r;
    if (oper == "-")
        return l-r;
    if (oper == "*")
        return l*r;
    else
        return l/r;
}
static bool isBaseOperator(const std::string &oper)
{
    return (oper == "+" || oper == "-" ||
            oper == "*" || oper == "/");
}

template<class T>
bool calcLogicalOperator(T l, T r, const std::string &oper)
{
    if (oper == ">")
        return l>r;
    if (oper == "<")
        return l<r;
    if (oper == ">=")
        return l>=r;
    if (oper == "<=")
        return l<=r;
    if (oper == "==")
        return l==r;
    if (oper == "||")
        return l||r;
    else
        return l&&r;
}
static bool isLogicalOperator(const std::string &oper)
{
    return (oper == ">" || oper == "<" ||
            oper == ">=" || oper == "<=" ||
            oper == "==" || oper == "||" || oper == "&&");
}

template <class T>
T calcBitOperator(T l, T r, const std::string &oper)
{
    if (oper == "|")
        return l|r;
    if (oper == "&")
        return l&r;
    if (oper == "^")
        return l^r;
    if (oper == "<<")
        return l<<r;
    else
        return l>>r;
}
static bool isBitOperator(const std::string &oper)
{
    return (oper == "|" || oper == "&" || oper == "^" ||
            oper == "<<" || oper == ">>");
}

template <class T>
static bool findChar(const std::string & s, T c)
{
    if (s.size() && (s.find(c) != std::string::npos))
        return true;
    return false;
}


static std::string operCheck(const std::string & oper, bool &has_minus)
{
    uint32_t pos = oper.find('-', 1);

    has_minus = pos==(oper.size()-1);
    if (has_minus)
        return oper.substr(0, pos);
    return oper;
}

#if __cplusplus >= 201103L
template<class T>
std::string DataToString(T v)
{
    char sz[32];
    if (std::is_same<T, float>::value ||
        std::is_same<T, double>::value)
    {
        sprintf(sz, "%ff", static_cast<double>(v));
    }
    else if (std::is_same<T, uint32_t>::value ||
             std::is_same<T, uint16_t>::value ||
             std::is_same<T, uint8_t>::value)
    {
        sprintf(sz, "%uu", v);
    }
    else {
        sprintf(sz, "%d", v);
    }

    return std::string(sz);
}
#else
static inline
std::string DataToString(double v)
{
    char sz[32];
    sprintf(sz, "%ff", v);
    return std::string(sz);
}
static inline
std::string DataToString(float v)
{
    return DataToString(static_cast<double>(v));
}

static inline
std::string DataToString(uint32_t v)
{
    char sz[32];
    sprintf(sz, "%uu", v);
    return std::string(sz);
}
static inline
std::string DataToString(uint16_t v)
{
    return DataToString(static_cast<uint32_t>(v));
}
static inline
std::string DataToString(uint8_t v)
{
    return DataToString(static_cast<uint32_t>(v));
}

static inline
std::string DataToString(int32_t v)
{
    char sz[32];
    sprintf(sz, "%d", v);
    return std::string(sz);
}
static inline
std::string DataToString(int16_t v)
{
    return DataToString(static_cast<int32_t>(v));
}
static inline
std::string DataToString(int8_t v)
{
    return DataToString(static_cast<int32_t>(v));
}
#endif

static bool _isFloat(const std::string &str)
{
    if (findChar(str, '.'))
        return true;
    if (findChar(str, 'f') && !(str[0]=='0' && str[1]=='x'))
        return true;
    return false;
}
static bool _isUnsigned(const std::string &str)
{
    if (findChar(str, 'u'))
        return true;
    if (str[0]=='0' && str[1]=='x')
        return true;
    return false;
}
static uint32_t _getUnsignedData(const std::string &str)
{
    uint32_t data;
    if (str[0]=='0' && str[1]=='x')
        sscanf(str.c_str(), "0x%x", &data);
    else
        sscanf(str.c_str(), "%u", &data);

    return data;
}
static int32_t _getSignedData(const std::string &str)
{
    int32_t data;
    sscanf(str.c_str(), "%d", &data);
    return data;
}
static float _getFloatData(const std::string &str)
{
    float data;
    if (_isFloat(str)) {
        sscanf(str.c_str(), "%f", &data);
    }
    else if (_isUnsigned(str)) {
        uint32_t d = _getUnsignedData(str);
        data = static_cast<float>(d);
    }
    else
        data = static_cast<float>(_getSignedData(str));

    return data;
}

static std::string CalcOperator(const std::string &l,
                                const std::string &r,
                                const std::string &oper)
{
    if (_isFloat(l) || _isFloat(r)) {
        float ld = _getFloatData(l);
        float rd = _getFloatData(r);

        bool has_minus = false;
        std::string o = operCheck(oper, has_minus);
        if (has_minus)
            rd = -rd;

        if (isBaseOperator(o)) {
            return DataToString(calcBaseOperator(ld, rd, oper));
        }
        else if (isLogicalOperator(o)) {
            uint32_t d1 = static_cast<uint32_t>(ld);
            uint32_t d2 = static_cast<uint32_t>(rd);
            return DataToString(calcLogicalOperator(d1, d2, o));
        }
        else if (isBitOperator(o)) {
            uint32_t d1 = static_cast<uint32_t>(ld);
            uint32_t d2 = static_cast<uint32_t>(rd);
            return DataToString(calcBitOperator(d1, d2, o));
        }
        else {
            printf("%s found execptions line:%d\n", __func__, __LINE__);
            return "";
        }
    }
    else if (_isUnsigned(l) || _isUnsigned(r)) {
        uint32_t ld = _getUnsignedData(l);
        uint32_t rd = _getUnsignedData(r);

        bool has_minus = false;
        std::string o = operCheck(oper, has_minus);
        if (has_minus)
            rd = -rd;

        if (isBaseOperator(o)) {
            return DataToString(calcBaseOperator(ld, rd, o));
        }
        else if (isLogicalOperator(o)) {
            return DataToString(calcLogicalOperator(ld, rd, o));
        }
        else if (isBitOperator(o)) {
            return DataToString(calcBitOperator(ld, rd, o));
        }
        else {
            printf("%s found execptions line:%d\n", __func__, __LINE__);
        }
    }
    else {
        int32_t ld = _getSignedData(l);
        int32_t rd = _getSignedData(r);

        bool has_minus = false;
        std::string o = operCheck(oper, has_minus);
        if (has_minus)
            rd = -rd;

        if (isBaseOperator(o)) {
            return DataToString(calcBaseOperator(ld, rd, o));
        }
        else if (isLogicalOperator(o)) {
            return DataToString(calcLogicalOperator(ld, rd, o));
        }
        else if (isBitOperator(o)) {
            return DataToString(calcBitOperator(ld, rd, o));
        }
        else {
            printf("%s found execptions line:%d\n", __func__, __LINE__);
        }
    }

    return "";
}

/*
 *  匹配最内侧的括号
 */
static bool mathBrackets(const std::string &str, strings &result)
{
    std::string::size_type pos = str.find_last_of('(');
    if (pos==std::string::npos) {
        return false;
    }

    std::string::size_type pos2 = str.find(')', pos+1);
    if (pos2==std::string::npos) {
        return false;
    }

    result.clear();
    result.resize(3);
    result[0] = str.substr(0, pos);
    result[1] = str.substr(pos+1, pos2-pos-1);
    result[2] = str.substr(pos2+1, str.size()-pos2-1);
    return true;
}

/*
 * 匹配最前面的运算符
 */
static bool matchTernaryOperator(const string &str, strings &result)
{
    size_t pos1 = str.find('?');
    if (pos1 == std::string::npos)
        return false;

    size_t pos2 = str.rfind(':');
    if (pos2 == std::string::npos) {
        printf("%s found execptions. exp:\"%s\"\n", __func__, str.c_str());
        return false;
    }

    result.clear();
    result.resize(3);
    result[0] = str.substr(0, pos1);
    result[1] = str.substr(pos1+1, pos2-pos1-1);
    result[2] = str.substr(pos2+1, str.size()-pos2-1);
    return true;
}

static bool isBinaryOperatorChar(char ch)
{
    static std::string binary("-+*/|&^><=");
    return binary.find(ch)!=std::string::npos;
}
/*
 *  匹配最前面的二元运算符，贪婪匹配（尽可能匹配多的）
 */
static bool matchBinaryOperation(const string &str, strings &result)
{
    if (str.size() <= 2)
        return false;

    result.clear();
    result.resize(4);

    uint32_t pos1 = 0;
    if (str[pos1] == '-') {
        pos1++;
    }
    if (isBinaryOperatorChar(str[pos1]))
        return false;

    pos1++;
    for (; pos1<str.size(); pos1++) {
        if (isBinaryOperatorChar(str[pos1]))
            break;
    }
    if (pos1 == str.size())
        return false;
    result[0] = str.substr(0, pos1);

    uint32_t pos2=pos1+1;
    for (; pos2<str.size(); pos2++) {
        if (!isBinaryOperatorChar(str[pos2]))
            break;
    }
    if (pos2 == str.size())
        return false;
    result[1] = str.substr(pos1, pos2-pos1);

    pos1 = pos2+1;
    for (; pos1<str.size(); pos1++) {
        if (isBinaryOperatorChar(str[pos1]))
            break;
    }
    result[2] = str.substr(pos2, pos1-pos2);
    if (pos1<str.size())
        result[3] = str.substr(pos1, str.size()-pos1);

    return true;
}

static std::string singleOperator(const std::string &str)
{
    if (str.size() < 2) {
        return str;
    }
    char ch = str[0];
    if (ch=='~' || ch=='!' || ch=='+') {
        uint32_t d;
        sscanf(str.c_str()+1, "%u", &d);
        uint32_t pos=1;
        for (; pos<str.size(); pos++)
            if (isBinaryOperatorChar(str[pos]))
                break;
        if (ch=='~')
            d = ~d;
        else if (ch=='!')
            d = !d;
        return DataToString(d) + str.substr(pos);
    }

    return str;
}

static std::string binayOperator(const std::string &str)
{
    strings result;

    std::string tmp = singleOperator(str);
    if (matchBinaryOperation(tmp, result)) {
        return binayOperator(CalcOperator(result[0], result[2], result[1])+result[3]);
    }
    else {
        return tmp;
    }
}

static string ternaryOperator(const string &str)
{
    strings result;
    if (::matchTernaryOperator(str, result)) {
        std::string tmp = binayOperator(result[0]);
        if (_getUnsignedData(tmp)) {
            return ternaryOperator(result[1]);
        }
        else
            return ternaryOperator(result[2]);
    }
    else
        return binayOperator(str);
}



std::string ExpParser::calcExp(const std::string &exp)
{
    std::string str(exp);
    std::string::size_type index = 0;
    while( (index = str.find(' ',index)) != std::string::npos)
        str.erase(index,1);
    transform(str.begin(), str.end(), str.begin(), ::tolower);

    if (str.empty())
        return "";

#ifdef CALC_INF_OUTPUT
    printf("parse: %s ++++++++++\n", str.c_str());
#endif
    string result;
    strings tmp;
    if (mathBrackets(str, tmp)) {
        std::string sub_exp = ternaryOperator(tmp[1]);
#ifdef CALC_INF_OUTPUT
        printf("calc: %s=%s\n", tmp[1].c_str(), sub_exp.c_str());
#endif
        result = calcExp(tmp[0] + sub_exp + tmp[2]);
    }
    else {
        result = ternaryOperator(str);
#ifdef CALC_INF_OUTPUT
        printf("calc: %s=%s\n", str.c_str(), result.c_str());
#endif
    }

    if (_isFloat(result)) {
        if (*result.rbegin() == 'f')
            result.erase(result.size()-1);
    }
    else if (_isUnsigned(result)) {
        if (*result.rbegin() == 'u')
            result.erase(result.size()-1);
    }

    return result;
}
